﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;

using TweetSharp;

namespace Twitter4Umbraco.Extensions
{
	/// <summary>
	/// A class of commonly used methods to handle Twitter status updates.
	/// </summary>
	public class Common
	{
		public const string TWITTER_URL = "http://twitter.com/";

		public const string API_KEY = "V3mCLrcXh7m7gDkiLgbqg";

		public const string CONSUMER_KEY = "V3mCLrcXh7m7gDkiLgbqg";

		public const string CONSUMER_SECRET = "mE6rFa9TnOY5d04b8mJDy69XYVB1VlEToWuOizxGVg";

		public const string REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token";

		public const string ACCESS_TOKEN_URL = "https://api.twitter.com/oauth/access_token";

		public const string AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize";

		/// <summary>
		/// Format Twitter Date
		/// </summary>
		/// <param name="twitterDate">Thu Nov 08 19:07:37 +0000 2007</param>
		/// <returns>2007-11-08T19:07:37</returns>
		public static string FormatTwitterDate(string twitterDate)
		{
			DateTime parsedDate;
			
			if (DateTime.TryParseExact(twitterDate, "ddd MMM dd HH:mm:ss zzz yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out parsedDate))
			{
				return parsedDate.ToLocalTime().ToString("s");
			}

			return "1900-01-01T00:00:00";
		}

		/// <summary>
		/// Format the tweet, with @reply links, #searches and external URLs
		/// </summary>
		/// <param name="twitterText">The original tweet</param>
		/// <returns>The new text with <a> tags added</returns>
		public static string FormatUrls(string twitterText)
		{
			// Chris Houston's code to fix the regex 
			// twitter user: @readingdancer
			try
			{
				var urlRegex = new Regex(@"(http\:\/\/\S+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
				var urlSecureRegex = new Regex(@"(https\:\/\/\S+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
				var replyRegex = new Regex(@"(^|\s)@(\w+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
				var searchRegex = new Regex(@"(^|\s)#(\w+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);

				twitterText = urlRegex.Replace(twitterText, " <a href=\"$1\">$1</a>");                                  // Replace URLs
				twitterText = urlSecureRegex.Replace(twitterText, " <a href=\"$1\">$1</a>");                            // Replace https URLs
				twitterText = replyRegex.Replace(twitterText, " <a href=\"http://twitter.com/$2\">@$2</a>");            // Replace @ replies
				twitterText = searchRegex.Replace(twitterText, " <a href=\"http://twitter.com/search?q=$2\">#$2</a>");  // Replace hash tags

				return twitterText;
			}
			catch (Exception)
			{
				//Return the original string
				return twitterText;
			}
		}

		public static string GetStatusUrl(string statusId, string username)
		{
			return string.Concat(TWITTER_URL, username, "/status/", statusId);
		}

		public static TwitterStatus GetTweet(string statusId)
		{
			long id;

			if (long.TryParse(statusId, out id))
			{
				var service = new TwitterService();
				return service.GetTweet(id);
			}

			return null;
		}

		public static string BakeTweet(string statusId)
		{
			var tweet = GetTweet(statusId);

			if (tweet != null)
			{
				return BakeTweet(tweet);
			}
			
			return string.Empty;
		}

		public static string BakeTweet(TwitterStatus tweet)
		{
			string userUrl = string.Concat(TWITTER_URL, tweet.User.ScreenName);
			string statusUrl = GetStatusUrl(tweet.Id.ToString(), tweet.User.ScreenName);

			var blackbird = new StringBuilder();

			blackbird.Append("<!-- ");
			blackbird.Append(statusUrl);
			blackbird.Append(" --> <style type='text/css'>.bbpBox");
			blackbird.Append(tweet.Id);
			blackbird.Append(" {background:url(");
			blackbird.Append(tweet.User.ProfileBackgroundImageUrl);
			blackbird.Append(") #");
			blackbird.Append(tweet.User.ProfileBackgroundColor);
			blackbird.Append(";padding:20px;} p.bbpTweet{background:#fff;padding:10px 12px 10px 12px;margin:0;min-height:48px;color:#");
			blackbird.Append(tweet.User.ProfileTextColor);
			blackbird.Append(";font-size:18px !important;line-height:22px;-moz-border-radius:5px;-webkit-border-radius:5px} p.bbpTweet span.metadata{display:block;width:100%;clear:both;margin-top:8px;padding-top:12px;height:40px;border-top:1px solid #fff;border-top:1px solid #e6e6e6} p.bbpTweet span.metadata span.author{line-height:19px} p.bbpTweet span.metadata span.author img{float:left;margin:0 7px 0 0px;width:38px;height:38px} p.bbpTweet a{color:#");
			blackbird.Append(tweet.User.ProfileLinkColor);
			blackbird.Append(" !important;} p.bbpTweet a:hover{text-decoration:underline}p.bbpTweet span.timestamp{font-size:12px;display:block}</style>");
			blackbird.Append("<div class='bbpBox");
			blackbird.Append(tweet.Id);
			blackbird.Append("'><p class='bbpTweet'>");
			blackbird.Append(tweet.Text);
			blackbird.Append("<span class='timestamp'><a title='");
			blackbird.Append(tweet.CreatedDate.ToString("ddd MMM dd HH:mm:ss zzz yyyy"));
			blackbird.Append("' href='");
			blackbird.Append(statusUrl);
			blackbird.Append("'>");
			blackbird.Append(GetPrettyDate(tweet.CreatedDate, "dd MMMM yyyy"));
			blackbird.Append("</a> via ");
			blackbird.Append(tweet.Source);
			blackbird.Append("</span><span class='metadata'><span class='author'><a href='");
			blackbird.Append(userUrl);
			blackbird.Append("'><img src='");
			blackbird.Append(tweet.User.ProfileImageUrl);
			blackbird.Append("' /></a><strong><a href='");
			blackbird.Append(userUrl);
			blackbird.Append("'>");
			blackbird.Append(tweet.User.Name);
			blackbird.Append("</a></strong><br/>");
			blackbird.Append(tweet.User.ScreenName);
			blackbird.Append("</span></span></p></div> <!-- end of tweet -->");

			return blackbird.ToString();
		}

		///<summary>
		/// Gets the ordinal suffix for a given date
		///</summary>
		///<param name="date">The date</param>
		///<returns>The ordinal suffix</returns>
		private static string GetDayNumberSuffix(DateTime date)
		{
			switch (date.Day)
			{
				case 1:
				case 21:
				case 31:
					return @"\s\t";
				case 2:
				case 22:
					return @"\n\d";
				case 3:
				case 23:
					return @"\r\d";
				default:
					return @"\t\h";
			}
		}

		/// <summary>
		/// Gets the pretty date.
		/// </summary>
		/// <param name="date">The date.</param>
		/// <returns>Returns a pretty date.</returns>
		public static string GetPrettyDate(string date)
		{
			return GetPrettyDate(date, "dd MMMM yyyy");
		}

		public static string GetPrettyDate(string date, string format)
		{
			// 0. Convert the String into DateTime
			DateTime d;

			if (DateTime.TryParse(date, out d))
			{
				return GetPrettyDate(d, format);
			}

			return date;
		}

		/// <summary>
		/// Gets the pretty date.
		/// </summary>
		/// <param name="date">The date.</param>
		/// <param name="format">The format.</param>
		/// <returns>Returns a pretty date.</returns>
		/// <remarks>
		/// http://dotnetperls.com/pretty-date
		/// http://ejohn.org/blog/javascript-pretty-date/
		/// </remarks>
		public static string GetPrettyDate(DateTime date, string format)
		{
			// 1. Get time span elapsed since the date.
			TimeSpan s = DateTime.Now.Subtract(date);

			// 2. Get total number of days elapsed.
			Int32 dayDiff = (Int32)s.TotalDays;

			// 3. Get total number of seconds elapsed.
			Int32 secDiff = (Int32)s.TotalSeconds;

			// 4. Don't allow out of range values.
			if (dayDiff < 0 || dayDiff >= 31)
			{
				return FormatDateTime(date, format); // d.ToString(format);
			}

			// 5. Handle same-day times.
			if (dayDiff == 0)
			{
				// A. Less than one minute ago.
				if (secDiff < 60)
				{
					return "just now";
				}

				// B. Less than 2 minutes ago.
				if (secDiff < 120)
				{
					return "1 minute ago";
				}

				// C.Less than one hour ago.
				if (secDiff < 3600)
				{
					return String.Format("{0} minutes ago", Math.Floor((double)secDiff / 60));
				}

				// D. Less than 2 hours ago.
				if (secDiff < 7200)
				{
					return "1 hour ago";
				}

				// E. Less than one day ago.
				if (secDiff < 86400)
				{
					return String.Format("{0} hours ago", Math.Floor((double)secDiff / 3600));
				}
			}

			// 6. Handle previous days.
			if (dayDiff == 1)
			{
				return "yesterday";
			}

			if (dayDiff < 7)
			{
				return String.Format("{0} days ago", dayDiff);
			}

			if (dayDiff < 31)
			{
				return String.Format("{0} weeks ago", Math.Ceiling((double)dayDiff / 7));
			}

			return FormatDateTime(date, format);
		}

		///<summary>
		/// Converts the value of the date time string to its equivalent string representation using the specified format.
		///</summary>
		///<param name="date">The date string</param>
		///<param name="format">The date format</param>
		///<returns>The formated date string</returns>
		public static string FormatDateTime(string date, string format)
		{
			DateTime d;

			if (DateTime.TryParse(date, out d) && !string.IsNullOrEmpty(format))
			{
				return FormatDateTime(date, format);
			}

			return date;
		}

		public static string FormatDateTime(DateTime date, string format)
		{
			// Replace ordinal token
			format = Regex.Replace(format, @"(?<!\\)((\\\\)*)(S)", "$1" + GetDayNumberSuffix(date));

			// Format remaining tokens
			return date.ToString(format);
		}
	}
}
